//! 文件存储的配置服务实现

use async_trait::async_trait;
use nacos_core::{ConfigStorage, ConfigItem, Result, Storage};
use std::sync::Arc;

use super::storage::FileStorage;

#[derive(Debug)]
pub struct FileConfigStorage {
    fs: Arc<FileStorage>,
}

impl FileConfigStorage {
    pub fn new(fs: Arc<FileStorage>) -> Self {
        Self { fs }
    }

    fn get_key(namespace: &str, group: &str, data_id: &str) -> String {
        format!("config/{}@@{}@@{}", namespace, group, data_id)
    }
}

#[async_trait]
impl ConfigStorage for FileConfigStorage {
    async fn get_config(&self, namespace: &str, group: &str, data_id: &str) -> Result<Option<ConfigItem>> {
        let key = Self::get_key(namespace, group, data_id);
        if let Some(bytes) = self.fs.get(&key).await? {
            Ok(serde_json::from_slice(&bytes).ok())
        } else {
            Ok(None)
        }
    }

    async fn save_config(&self, config: &ConfigItem) -> Result<()> {
        let key = Self::get_key(&config.namespace, &config.group, &config.data_id);
        let bytes = serde_json::to_vec(config)?;
        self.fs.put(&key, &bytes).await
    }

    async fn delete_config(&self, namespace: &str, group: &str, data_id: &str) -> Result<()> {
        let key = Self::get_key(namespace, group, data_id);
        self.fs.delete(&key).await
    }

    async fn list_configs(&self, namespace: &str, group: Option<&str>) -> Result<Vec<ConfigItem>> {
        let prefix = match group {
            Some(g) => format!("config/{}@@{}@@", namespace, g),
            None => format!("config/{}@@", namespace),
        };
        let all_items = self.fs.scan(&prefix).await?;
        let result = all_items.into_iter()
            .filter_map(|(_, v)| serde_json::from_slice::<ConfigItem>(&v).ok())
            .collect();
        Ok(result)
    }

    async fn get_config_history(&self, _namespace: &str, _group: &str, _data_id: &str, _limit: usize) -> Result<Vec<ConfigItem>> {
        // File storage does not support history in this version
        Ok(vec![])
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use nacos_core::ConfigItem;
    use tempfile::tempdir;

    async fn create_test_storage() -> FileConfigStorage {
        let dir = tempdir().unwrap();
        let file_storage = Arc::new(FileStorage::new(dir.path()).await.unwrap());
        FileConfigStorage::new(file_storage)
    }

    fn create_test_config(data_id: &str, group: &str, content: &str) -> ConfigItem {
        let mut config = ConfigItem::new(data_id.to_string(), group.to_string(), content.to_string());
        config.namespace = "public".to_string();
        config
    }

    #[tokio::test]
    async fn test_save_and_get_config() {
        let storage = create_test_storage().await;
        let config = create_test_config("test-id", "test-group", "content");

        storage.save_config(&config).await.unwrap();

        let retrieved = storage.get_config("public", "test-group", "test-id").await.unwrap().unwrap();
        assert_eq!(config, retrieved);
    }

    #[tokio::test]
    async fn test_delete_config() {
        let storage = create_test_storage().await;
        let config = create_test_config("test-id", "test-group", "content");
        storage.save_config(&config).await.unwrap();

        storage.delete_config("public", "test-group", "test-id").await.unwrap();

        let retrieved = storage.get_config("public", "test-group", "test-id").await.unwrap();
        assert!(retrieved.is_none());
    }

    #[tokio::test]
    async fn test_list_configs() {
        let storage = create_test_storage().await;
        storage.save_config(&create_test_config("id1", "group1", "c1")).await.unwrap();
        storage.save_config(&create_test_config("id2", "group1", "c2")).await.unwrap();
        let mut other_ns = create_test_config("id3", "group1", "c3");
        other_ns.namespace = "other".to_string();
        storage.save_config(&other_ns).await.unwrap();

        let group1_configs = storage.list_configs("public", Some("group1")).await.unwrap();
        assert_eq!(group1_configs.len(), 2);

        let all_public_configs = storage.list_configs("public", None).await.unwrap();
        assert_eq!(all_public_configs.len(), 2);
    }
}